/*		<one line to give the program's name and a brief idea of what it does.>
 * Copyright (C) <2025>  <zhen>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.*/

#include "can/Xcanmsg.h"
#include "Xtools.h"
#include <string.h>
#include <time.h>

/***
 * 毫秒延时
 */
void X_delayMs(uint32_t ms)
{
    struct timespec ts = {
        .tv_sec = ms / 1000,
        .tv_nsec = (ms % 1000) * 1000000};
    nanosleep(&ts, NULL);
}

/***
 * 将CAN数据段长度转换成dlc
 */
uint8_t len2dlc(uint8_t len)
{
    // 通过数据长度返回对应DLC
    if (len > 64)
    {
        X_err("len > 64");
        return 0;
    }
    if (len <= 8)
    {
        return len;
    }
    len -= 8;
    len /= 4;
    return len + 9;
}

/***
 * 将CAN DLC 转换成实际数据长度
 */
uint8_t dlc2len(uint8_t dlc)
{
    // 通过dlc返回数据长度
    if (dlc <= 8)
    {
        return dlc;
    }
    dlc -= 8;
    dlc *= 4;
    return dlc + 8;
}

/***
 * 序列化CAN报文，
 * @param msg CAN报文
 * @param fp 输出位置
 */
void dumpCanMsg(FILE *fp, canMsg_t *msg)
{
    if (msg->FDF == 0)
    {
        fprintf(fp, "CAN :\t");
    }
    else
    {
        fprintf(fp, "CAN FD :\t");
    }
    fprintf(fp,"%d:%06d\r\n",msg->tv.tv_sec,msg->tv.tv_usec);
    if (msg->IDE == 0)
    {
        fprintf(fp, "\t标准帧\tID : 0x%X\r\n", msg->ID);
    }
    else
    {
        fprintf(fp, "\t扩展帧\tID : 0x%Xx\r\n", msg->ID);
    }
    uint8_t len = dlc2len(msg->DLC);
    fprintf(fp, "\t数据:\r\n\t\t");
    for (int i = 0; i < len; i++)
    {
        fprintf(fp, "%02X ", msg->Data[i]);
        if (i % 16 == 15)
        {
            fprintf(fp, "\r\n\t\t");
        }
    }
    fprintf(fp, "\r\n");
    return;
}

/***
 * 用来创建一个CAN邮箱，
 */
struct canMsgMail *createCanMsgMail(void)
{
    struct canMsgMail *mail;
    mail = (struct canMsgMail *)malloc(sizeof(struct canMsgMail));
    memset(mail, 0, sizeof(struct canMsgMail));
    XListInit(struct canMsgMail *,mail,list);
    pthread_spin_init(&mail->spin, PTHREAD_PROCESS_PRIVATE);
    // 邮箱是空的
    mail->head = 0;
    mail->tail = 0;
    return mail;
}

/***
 * 销毁一个CAN邮箱
 */
void destroyCanMsgMail(struct canMsgMail *mail)
{
    // 判断是否连接在其他位置
    pthread_spin_destroy(&mail->spin);
    XListRem(struct canMsgMail *,mail,list);
    free(mail);
}

// 发送报文，检查环形缓冲区是否已满，没满的话往里边添加
X_s sendCanMsg(struct canMsgMail *mail, canMsg_t *msg)
{
    // 判断邮箱是否已满
    int sel = mail->tail + 1;
    if(sel == mail_depth){
        sel = 0;
    }
    if(sel == mail->head){
        // 环形缓冲区已满
        return X_full;
    }
    // 加锁
    pthread_spin_lock(&mail->spin);
    // 写入
    mail->msgs[sel] = *msg;
    mail->tail = sel;
    // 释放锁
    pthread_spin_unlock(&mail->spin);
    return X_ok;
}

// 接收，从缓冲区接收数据
X_s rescvCanMsg(struct canMsgMail *mail, canMsg_t *msg, uint32_t timeout)
{

    do
    {
        // 判断是否有数据
        if (mail->head != mail->tail)
        {
            // 有数据
            // 获取锁
            pthread_spin_lock(&mail->spin);
            mail->head++;
            if(mail->head == mail_depth){
                mail->head = 0;
            }
            *msg = mail->msgs[mail->head];
            // 释放锁
            pthread_spin_unlock(&mail->spin);
            return X_ok;
        }
        else
        {
            // 没有数据，延时等待
            X_delayMs(1);
        }
    } while (timeout--);
    return X_timeout;
}